package com.sce.client;

import java.util.HashMap;
import java.util.Map;

import com.smartgwt.client.core.Function;
import com.smartgwt.client.widgets.HTMLPane;

public class TextCodeArea extends HTMLPane {
	
	//private String initContents;
	private String idEditor;
	
	private Theme DEFAULT_THEME = Theme.CHROME;
	private Mode DEFAULT_MODE = Mode.JAVA_SCRIPT;
	private String DEFAULT_FONT_SIZE = "12";
	private String DEFAULT_CONTENT = "";
	private boolean DEFAULT_CAN_EDIT = true;
	private boolean DEFAULT_CAN_SHOW_PRINT_MARGIN = true;
	private boolean DEFAULT_CAN_HIGHLIGHT_ACTIVE_LINE = true;
	private boolean DEFAULT_CAN_WRAP = false;
	private int DEFAULT_TAB_SIZE = 3;
	private boolean DEFAULT_AUTO_COMPLETE = true;
	
	
	private static final String divEditor = "<div id=':idEditor'" +
		" style='POSITION:relative;display:inline-block;width:100%;" +
		" height:100%;vertical-align:top;VISIBILITY:inherit;'></div>";
	private static final String autocomplete = "<style type='text/css'>.ace_autocomplete{z-index:300000 !important;}</style>";
	
	public TextCodeArea() {
		super();
		idEditor = "cod_editor_" + Double.valueOf(Math.random() * 1000000).longValue();
		super.setContents(autocomplete + divEditor.replace(":idEditor", idEditor));
		doOnRender(new Function() {
    		@Override
    		public void execute() {
    			setAceContext(idEditor);
    			setTheme(DEFAULT_THEME);
    			setMode(DEFAULT_MODE);
    			setFontSize(DEFAULT_FONT_SIZE);
    			setContents(DEFAULT_CONTENT);
    			setCanEdit(DEFAULT_CAN_EDIT);
    			setCanShowPrintMargin(DEFAULT_CAN_SHOW_PRINT_MARGIN);
    			setCanHighlightActiveLine(DEFAULT_CAN_HIGHLIGHT_ACTIVE_LINE);
    			setCanWrap(DEFAULT_CAN_WRAP);
    			setTabSize(DEFAULT_TAB_SIZE);
    			setAutocomplete(DEFAULT_AUTO_COMPLETE);
    			aceBind();
    			aceCommand();
    		}
    	});
	}
    
    private native void setAceContext(String elementId) /*-{
    	var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
		self.editor = $wnd.ace.edit(elementId);
		self.session = self.editor.getSession();
	}-*/;
    
    @Override
    public String getContents() {
    	return (isDrawn())? getNativeContents() : DEFAULT_CONTENT;
	};
    
    private native String getNativeContents() /*-{
    	var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
    	if (self.session === undefined) {
    		return this.@com.sce.client.TextCodeArea::DEFAULT_CONTENT;
    	} else {
			return self.session.getValue();
		}
	}-*/;
    
    @Override
    public void setContents(String value) {
    	if (isDrawn()) {
    		setNativeContents(value);
    	} else {
    		DEFAULT_CONTENT = value;
    	}
    }
    
    private native void setNativeContents(String value) /*-{
		var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
    	if (self.session === undefined) {
    		this.@com.sce.client.TextCodeArea::DEFAULT_CONTENT = value;
    	} else {
			self.session.setValue(value);
		}
	}-*/;

    public native void setCanEdit(boolean canEdit) /*-{
    	var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
    	if (self.editor === undefined) {
    		this.@com.sce.client.TextCodeArea::DEFAULT_CAN_EDIT = canEdit;
    	} else {
			self.editor.setReadOnly(!canEdit);
		}
	}-*/;
    
    public native boolean getCanEdit() /*-{
    	var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
    	if (self.editor === undefined) {
    		return this.@com.sce.client.TextCodeArea::DEFAULT_CAN_EDIT;
    	} else {
			return self.editor.getReadOnly();
		}
    }-*/;
    
    public native void setCanShowPrintMargin(boolean showPrintMargin) /*-{
    	var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
    	if (self.editor === undefined) {
    		this.@com.sce.client.TextCodeArea::DEFAULT_CAN_SHOW_PRINT_MARGIN = showPrintMargin;
    	} else {
			self.editor.setShowPrintMargin(showPrintMargin);
		}
	}-*/;
    
    public native boolean getCanShowPrintMargin() /*-{
    	var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
    	if (self.editor === undefined) {
    		return this.@com.sce.client.TextCodeArea::DEFAULT_CAN_SHOW_PRINT_MARGIN;
    	} else {
			return self.editor.getShowPrintMargin();
		}
    }-*/;
    
    public native void setCanHighlightActiveLine(boolean highlightActiveLine) /*-{
    	var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
    	if (self.editor === undefined) {
    		this.@com.sce.client.TextCodeArea::DEFAULT_CAN_HIGHLIGHT_ACTIVE_LINE = highlightActiveLine;
    	} else {
			self.editor.setHighlightActiveLine(highlightActiveLine);
		}
	}-*/;
    
    public native boolean getCanHighlightActiveLine() /*-{
    	var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
    	if (self.editor === undefined) {
    		return this.@com.sce.client.TextCodeArea::DEFAULT_CAN_HIGHLIGHT_ACTIVE_LINE;
    	} else {
			return self.editor.getHighlightActiveLine();
		}
    }-*/;
    
    public native void setCanWrap(boolean wrap) /*-{
    	var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
    	if (self.session === undefined) {
    		this.@com.sce.client.TextCodeArea::DEFAULT_CAN_WRAP = wrap;
    	} else {
			self.session.setUseWrapMode(wrap);
		}
	}-*/;
    
    public native boolean getCanWrap() /*-{
    	var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
    	if (self.session === undefined) {
    		return this.@com.sce.client.TextCodeArea::DEFAULT_CAN_WRAP;
    	} else {
			return self.session.getUseWrapMode();
		}
    }-*/;
    
    public native void setTabSize(int tabSize) /*-{
    	var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
    	if (self.session === undefined) {
    		this.@com.sce.client.TextCodeArea::DEFAULT_TAB_SIZE = tabSize;
    	} else {
			self.session.setTabSize(tabSize);
		}
	}-*/;
    
    public native void getTabSize() /*-{
    	var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
    	if (self.session === undefined) {
    		return this.@com.sce.client.TextCodeArea::DEFAULT_TAB_SIZE;
    	} else {
			return self.session.getTabSize();
		}
    }-*/;
    
    public native int getNumberOfLines() /*-{
    	var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
		return self.session.getLength();
	}-*/;
    
    public native void goToLine(int lineNumber) /*-{
    	var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
		self.editor.gotoLine(lineNumber);
	}-*/;
    
    public native void showLineNumber(boolean flag) /*-{
    	var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
    	self.editor.setShowPrintMargin(flag);
    }-*/;
    
    public native void appendAtCursor(String content) /*-{
    	var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
		self.editor.insert(content);
	}-*/;
    
    public void setTheme(Theme theme) {
    	setTheme(theme.getValue());
    }
    
    private native void setTheme(String theme) /*-{
    	var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
    	if (self.editor === undefined) {
    		this.@com.sce.client.TextCodeArea::DEFAULT_THEME = @com.sce.client.Theme::getByValue(Ljava/lang/String;)(theme);
    	} else {
    		self.editor.setTheme(theme);
		}
	}-*/;
    
    public native Theme getTheme() /*-{
		var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
		if (self.editor === undefined) {
    		return this.@com.sce.client.TextCodeArea::DEFAULT_THEME;
    	} else {
			return @com.sce.client.Theme::getByValue(Ljava/lang/String;)(self.editor.getTheme());
		}
	}-*/;
    
    public native void setMode(Mode mode) /*-{
    	var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
    	if (self.session === undefined) {
    		this.@com.sce.client.TextCodeArea::DEFAULT_MODE = mode;
    	} else {
			self.session.setMode(mode.@com.sce.client.Mode::getValue()());
		}
	}-*/;
    
    public native Mode getMode() /*-{
    	var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
    	if (self.session === undefined) {
    		return this.@com.sce.client.TextCodeArea::DEFAULT_MODE;
    	} else {
			return @com.sce.client.Mode::getByValue(Ljava/lang/String;)(self.session.getMode());
		}
    }-*/;
    
    public native void setFontSize(String fontSize) /*-{
    	var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
    	if (self.editor === undefined) {
    		this.@com.sce.client.TextCodeArea::DEFAULT_FONT_SIZE = fontSize;
    	} else {
    		self.editor.setFontSize((fontSize.indexOf("px") > -1)? fontSize : fontSize + "px");
		}
	}-*/;
    
    private native String getFontSize() /*-{
		var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
		if (self.editor === undefined) {
    		return this.@com.sce.client.TextCodeArea::DEFAULT_FONT_SIZE;
    	} else {
			return self.editor.getFontSize();
		}
	}-*/;
    
    public native void setAutocomplete(boolean flag) /*-{
    	var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
    	this.@com.sce.client.TextCodeArea::DEFAULT_AUTO_COMPLETE = flag;
    	if (self.editor != undefined) {
			self.editor.setOptions({enableBasicAutocompletion:flag, enableSnippets:flag, enableLiveAutocompletion:false});
			//if ('ace/mode/javascript' === self.editor.getOption('mode')) {
				$wnd.ace.require("ace/ext/language_tools").addCompleter({
					getCompletions: function(editor, session, pos, prefix, callback) {
						if (prefix.length === 0) { 
							callback(null, []); return 
						}
						$wnd.$.getJSON(
							"http://rhymebrain.com/talk?function=getRhymes&word=" + prefix,
			                function(wordList) {
			                    callback(null, wordList.map(function(ea) {
			                        return {name: ea.word, value: ea.word, score: ea.score, meta: "rhyme"}
			                    }));
			                }
						)
					}
				});
			//}
		}
	}-*/;
    
    public boolean getAutocomplete() {
		return DEFAULT_AUTO_COMPLETE;
	}
    
    private Map<String, Function> commandHandlers = new HashMap<String, Function>();
    private Map<String, Function> bindHandlers = new HashMap<String, Function>();
    
    public void addAceCommand(String keys, Function function) {
    	commandHandlers.put(keys, function);
	}
    
    public void addAceBind(String type, Function function) {
    	bindHandlers.put(type, function);
	}
    
    private void aceCommand() {
    	for (String keys : commandHandlers.keySet()) {
    		addNativeAceCommand(keys, commandHandlers.get(keys));
		}
    }
    
    private void aceBind() {
    	for (String type : bindHandlers.keySet()) {
    		addNativeAceBind(type, bindHandlers.get(type));
		}
    }
    
    private native void addNativeAceCommand(String keys, Function func) /*-{
		var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
		var cmd_name = "replace_" + Math.floor(Math.random() * 1000000);
		self.editor.commands.addCommand({
			name: cmd_name,
			bindKey: {win: keys},
			exec: function(e) { 
				func.@com.smartgwt.client.core.Function::execute()();
			}
		});
		//, mac: "Command-Option-Enter"
	}-*/;
    
    private native void addNativeAceBind(String type, Function func) /*-{
    	var self = this.@com.smartgwt.client.widgets.BaseWidget::getOrCreateJsObj()();
    	self.session.on(type, function(e) { 
    		func.@com.smartgwt.client.core.Function::execute()();
    	});
    }-*/;
    
    
    
}